package me.luger.core.commom.util;

import org.apache.commons.lang3.StringUtils;

import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * DateUtil
 *
 * @author luger
 * @date 2016/8/16 0016
 */
public class DateUtil {
    public static final String DATE_YYYYMMDD = "yyyyMMdd";
    public static final String DATE_YYYYMM = "yyyyMM";
    public static final String DATE_YYYYMMDDHH = "yyyyMMddHH";
    public static final String DATE_YYYY_MM_DD_HH = "yyyy-MM-dd HH";
    public static final String DATE_TIME_YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
    public static final String DATE_TIME_YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
    public static final String DATE_TIME_YYYYMMDDHHMMSSSSS = "yyyyMMddHHmmssSSS";
    public static final String DATE_TIME_YYYYMMDDHHMM = "yyyyMMddHHmm";
    public static final String DATE_TIME_YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
    public static final String DATE_TIME_YYYY_MM_DD_HH_MM_SS_SSS = "yyyy-MM-dd HH:mm:ss,SSS";
    public static final String DATE_YYYY_MM_DD = "yyyy-MM-dd";
    public static final String DATE_YYYY = "yyyy";
    public static final String DATE_MM = "MM";
    public static final String DATE_DD = "dd";

    public static final Long DAY_MILLIS = 3600l * 1000l * 24l;

    public static ThreadLocal<Map<String, SimpleDateFormat>> FORMAT_MAP = null;

    private static ThreadLocal<Long> timeMarker = new ThreadLocal<Long>();

    static {
        FORMAT_MAP = new ThreadLocal<Map<String, SimpleDateFormat>>() {
            @Override
            protected Map<String, SimpleDateFormat> initialValue() {
                Map<String, SimpleDateFormat> formatMap = new HashMap<String, SimpleDateFormat>();
                formatMap.put(DATE_YYYYMMDD,
                        new SimpleDateFormat(DATE_YYYYMMDD));
                formatMap.put(DATE_TIME_YYYY_MM_DD_HH_MM_SS,
                        new SimpleDateFormat(DATE_TIME_YYYY_MM_DD_HH_MM_SS));
                formatMap.put(DATE_TIME_YYYYMMDDHHMMSS, new SimpleDateFormat(
                        DATE_TIME_YYYYMMDDHHMMSS));
                formatMap.put(DATE_TIME_YYYYMMDDHHMMSSSSS,
                        new SimpleDateFormat(DATE_TIME_YYYYMMDDHHMMSSSSS));
                formatMap.put(DATE_TIME_YYYYMMDDHHMM, new SimpleDateFormat(
                        DATE_TIME_YYYYMMDDHHMM));
                formatMap
                        .put(DATE_TIME_YYYY_MM_DD_HH_MM_SS_SSS,
                                new SimpleDateFormat(
                                        DATE_TIME_YYYY_MM_DD_HH_MM_SS_SSS));
                formatMap.put(DATE_YYYY_MM_DD, new SimpleDateFormat(
                        DATE_YYYY_MM_DD));
                formatMap.put(DATE_YYYY, new SimpleDateFormat(DATE_YYYY));
                formatMap.put(DATE_MM, new SimpleDateFormat(DATE_MM));
                formatMap.put(DATE_DD, new SimpleDateFormat(DATE_DD));
                formatMap.put(DATE_YYYYMMDDHH, new SimpleDateFormat(
                        DATE_YYYYMMDDHH));
                formatMap.put(DATE_TIME_YYYY_MM_DD_HH_MM, new SimpleDateFormat(
                        DATE_TIME_YYYY_MM_DD_HH_MM));
                formatMap.put(DATE_YYYY_MM_DD_HH, new SimpleDateFormat(
                        DATE_YYYY_MM_DD_HH));

                return formatMap;
            }
        };
    }

    public static Long getDayOfYearToTimeMillis(Integer year, Integer dayOfYear) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.DAY_OF_YEAR, dayOfYear);
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTimeInMillis();
    }

    public static Integer getDayOfYear(String dateTime, String dateFormat) {
        Calendar calendar = toCalendar(dateTime, dateFormat);
        return calendar.get(Calendar.DAY_OF_YEAR);
    }

    public static Integer getDayOfYear(Long timeMillis) {
        Calendar calendar = toCalendar(timeMillis);
        return calendar.get(Calendar.DAY_OF_YEAR);
    }

    public static Date getLastDayOfWeek(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setFirstDayOfWeek(Calendar.MONDAY);
        calendar.setTime(date);
        calendar.set(Calendar.DAY_OF_WEEK, calendar.getFirstDayOfWeek() + 6);
        return calendar.getTime();
    }

    public static Long getLastMillisToday() {
        String toDaylastTimeStr = formatNow(DATE_YYYYMMDD) + "235959999";
        return toTimeMillis(toDaylastTimeStr, DATE_TIME_YYYYMMDDHHMMSSSSS);
    }

    public static Long getLastSecondToday() {
        return getLastMillisToday() / 1000;
    }

    public static Long toTimeMillis(String time, String dateFormat) {
        Date date = parseDate(time, dateFormat);
        if (date == null) {
            return null;
        }
        return date.getTime();
    }

    public static String formatNow(String dateFormat) {
        Date date = new Date();
        return format(date, dateFormat);
    }

    public static String nowWithFormat(){
        Date date = new Date();
        return format(date, DATE_TIME_YYYY_MM_DD_HH_MM_SS_SSS);
    }

    public static Date nowAsDate() {
        return Calendar.getInstance().getTime();
    }

    public static Calendar nowAsCalendar() {
        return Calendar.getInstance();
    }
    public static Long nowAsLong(String dateFormat) {
        String dateStr = formatNow(dateFormat);
        return Long.parseLong(dateStr);
    }

    public static String format(Date date, String dateFormat) {
        DateFormat simpleDateFormat = getDateFormat(dateFormat);
        return simpleDateFormat.format(date);
    }

    public static String format(Long timeMillis, String dateFormat) {
        try {
            DateFormat simpleDateFormat = getDateFormat(dateFormat);
            return simpleDateFormat.format(timeMillis);
        } catch (Exception e) {
            LogUtil.errorLog(e, null);
            return null;
        }
    }

    public static String format(Calendar calendar, String dateFormat) {
        return format(calendar.getTime(), dateFormat);
    }

    public static String format(String time, String fromDateFormat,
                                String toDateFormat) {
        Date date = parseDate(time, fromDateFormat);
        return format(date, toDateFormat);
    }

    public static Long formatAsLong(Long timeMillis, String dateFormat) {
        String dateStr = format(timeMillis, dateFormat);
        return Long.parseLong(dateStr);
    }

    public static Long formatAsLong(Calendar calendar, String dateFormat) {
        String dateStr = format(calendar, dateFormat);
        return Long.parseLong(dateStr);
    }

    public static Long formatAsLong(Date date, String dateFormat) {
        String dateStr = format(date, dateFormat);
        return Long.parseLong(dateStr);
    }

    public static Date parseDate(String time, String dateFormat) {
        try {
            DateFormat simpleDateFormat = getDateFormat(dateFormat);
            return simpleDateFormat.parse(time);
        } catch (Exception e) {
            LogUtil.errorLog(e, null);
            return null;
        }
    }

    public static Date newDate(Integer year,Integer month,Integer day){
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR,year);
        calendar.set(Calendar.MONTH,month-1);
        calendar.set(Calendar.DAY_OF_MONTH,day);
        calendar.set(Calendar.HOUR_OF_DAY,0);
        calendar.set(Calendar.MINUTE,0);
        calendar.set(Calendar.SECOND,0);
        calendar.set(Calendar.MILLISECOND,0);
        return calendar.getTime();
    }

    public static Calendar toCalendar(Long dateTime) {
        try {
            Date date = new Date(dateTime);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            return calendar;
        } catch (Exception e) {
            LogUtil.errorLog(e, null);
            return null;
        }
    }

    public static Calendar toCalendar(String dateTime, String dateFormat) {
        try {
            DateFormat simpleDateFormat = getDateFormat(dateFormat);
            Date date = simpleDateFormat.parse(dateTime);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            return calendar;
        } catch (Exception e) {
            LogUtil.errorLog(e, null);
            return null;
        }
    }

    public static Calendar toCalendar(Long dateTime, String dateFormat) {
        return toCalendar(String.valueOf(dateTime), dateFormat);
    }

    public static Integer getDaysOfYear(Integer year) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        return calendar.getActualMaximum(Calendar.DAY_OF_YEAR);
    }

    private static DateFormat getDateFormat(String dateFormat) {
        Map<String, SimpleDateFormat> formatMap = FORMAT_MAP.get();
        DateFormat simpleDateFormat = formatMap.get(dateFormat);
        if (simpleDateFormat == null) {
            simpleDateFormat = new SimpleDateFormat(dateFormat);
        }
        return simpleDateFormat;
    }

    public static Date getYesterday() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        return calendar.getTime();
    }

    public static String secondToStr(String duration) {
        String timeStr = "";
        if (StringUtils.isNotBlank(duration)) {
            int second = Integer.parseInt(duration);
            int fen = second / 60;
            int mod = second % 60;
            timeStr = (fen < 10 ? "0" + fen : String.valueOf(fen)) + ":"
                    + (mod < 10 ? "0" + mod : String.valueOf(mod));
        }
        return timeStr;
    }
    public static Date getDateTime(int index) throws ParseException {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd 00:00:00");
        Calendar calendar = new GregorianCalendar();
        calendar.setTime(new Date());
        calendar.add(calendar.DATE, index);
        String dateString = sf.format(calendar.getTime());
        return sf.parse(dateString);
    }
    public static Date getDayOfMonth(int indexOfMonth, int indexDayOfCurrentMonth) {
        Calendar cal_1 = Calendar.getInstance();// 获取当前日期
        cal_1.add(Calendar.MONTH, indexOfMonth);
        cal_1.set(Calendar.DAY_OF_MONTH,indexDayOfCurrentMonth);// 设置为1号,当前日期既为本月第一天
        return cal_1.getTime();
    }
    /**
     * 获得本月最后一天23:59:59的剩余秒数
     *
     * @return
     */
    public static  long getRestSeconds4CurrentMonth() {
        Calendar calendar = Calendar.getInstance();
        calendar.set( calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.getActualMaximum(Calendar.DAY_OF_MONTH), 23, 59, 59);
        return calendar.getTimeInMillis()/1000;
    }
    /**
     * 获取距离本日23:59:59的秒数
     *
     * @return
     */
    public static long getRestSeconds4Today() {
        Calendar calendar = Calendar.getInstance();
        calendar.set( calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
        return calendar.getTimeInMillis()/1000;
    }
    /**
     * 判断某个日期(yyyyMMdd)是否在某个月份(yyyyMM)内
     * @param date
     * @param currentMonth
     * @return
     */
    public static boolean isDateInCurrentMonth(String date,String currentMonth){
        String tempDate = date.substring(0, date.length()-2);
        boolean isIn = tempDate.equals(currentMonth);
        return isIn;
    }

    /**
     * 根据当前日志做加减计算
     * @param field
     * @param amount
     * @return
     */
    public static Date add(int field, int amount){
        Calendar c = Calendar.getInstance();
        c.add(field,amount);
        return c.getTime();
    }

    public static Date add(Date date,int second){
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.add(Calendar.SECOND,second);
        return c.getTime();
    }

    /**
     * 耗时时间记录器
     * @param isLast 是否为最后一次调用，true的话为最后一次调用，会释放threadlocal
     * @return
     */
    public static Long timeMarker(boolean isLast){
        Long lastTime = timeMarker.get();
        Long now = System.currentTimeMillis();
        timeMarker.set(now);
        if(null == lastTime){
            return -1L;
        }
        Long use = now - lastTime;
        LogUtil.debugLog(String.format("start:%s end:%s; use[%s]ms",lastTime,now,use));
        if(isLast){
            timeMarker.remove();
        }
        return use;
    }

    /**
     * 释放 耗时时间记录器
     */
    public static void removeTimeMarker(){
        timeMarker.remove();
    }

    /**
     * 判断是否是今天
     * @param year
     * @param month
     * @param day
     * @return
     */
    public static boolean isCurrentDay(int year,int month,int day){
        Calendar now = nowAsCalendar();
        if(now.get(Calendar.YEAR) == year && now.get(Calendar.MONTH) == month-1 && now.get(Calendar.DAY_OF_MONTH) == day){
            return true;
        }
        return false;
    }

    /**
     * 判断当前日期是否在startDate和endDate之内
     * @param startDate
     * @param endDate
     * @return
     */
    public static boolean isInBetweenDate(Date startDate,Date endDate){
        Date now = nowAsDate();
        if(now.after(startDate) && now.before(endDate)){
            return true;
        }
        return false;
    }

    public static void main(String args[]){
        Date now = DateUtil.nowAsDate();
        now = DateUtil.add(now,24*60*60);
        System.out.println(DateUtil.format(now,DateUtil.DATE_TIME_YYYY_MM_DD_HH_MM_SS));

        System.out.println(DateUtil.isCurrentDay(2017,5,5));

        Date d = newDate(2017,2,31);
        System.out.println(format(d,DateUtil.DATE_TIME_YYYY_MM_DD_HH_MM_SS_SSS));


    }

}
